第一章 PyQt5的起点¶
1.1 开始安装PyQt5¶
就跟安装其他库的方法一样,非常简单:
Windows上安装:
pip install pyqt5
Linux上安装:
sudo apt-get install python3-pyqt5
MacOS上安装:
pip3 install pyqt5
验证是否安装成功,只需要导入下该模块就可以(注意大小写),如果没有出现任何错误提示,则安装成功:
1.2 程序运行起点¶
通过下方代码就可以呈现一个非常简单的PyQt5程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import sys from PyQt5.QtWidgets import QApplication, QLabel if __name__ == '__main__': #1. 想要创建应用必须先实例化一个QApplication, #并将sys.argv作为参数传入; #2. 实例化一个QLabel控件, #该控件用来展示文字或图片(可以想象下衣服标签,上面既有文字也有图片), #这里用于展示文本。 #可以像上方代码一样直接传入'Hello World'进行实例化, #也可以先实例化,再调用`setText()`方法来设置文本: app = QApplication(sys.argv) #1 label = QLabel('Hello World') # 2 label.show() # 3 sys.exit(app.exec_()) # 4 |
-
- 想要创建应用必须先实例化一个
QApplication
,并将sys.argv
作为参数传入;
- 想要创建应用必须先实例化一个
-
- 实例化一个
QLabel
控件,该控件用来展示文字或图片(可以想象下衣服标签,上面既有文字也有图片),这里用于展示文本。可以像上方代码一样直接传入'Hello World'进行实例化,也可以先实例化,再调用setText()
方法来设置文本:
- 实例化一个
1 2 3 4 5 6 7 8 9 10 | import sys from PyQt5.QtWidgets import QApplication, QLabel if __name__ == '__main__': app = QApplication(sys.argv) #实例化QApplication label = QLabel() #实例化Qlabel label.setText('Hello World') #设置文本 label.show() #设置显示 sys.exit(app.exec_()) #当app.exec_() 返回值为0时系统退出 |
-
- 通过调用show()方法使控件可见(默认是隐藏);
-
4.
app.exec_()
是执行应用,让应用开始运转循环,直到窗口关闭返回0
给sys.exit()
,退出整个程序。 有些小伙伴可能发现还有exec()
,在Python2中exec是关键字,所以PyQt5就使用exec_()
而不是exec()
。不过exec
在Python3中已经不再是关键字了,所以如果读者使用的是Python3的话那在上述代码中用`exec()也完全没关系。
那么这个程序运行起来就是这样的:
我们可以直接在字符串中加上html代码,修改文本样式。
1 2 3 4 5 6 7 8 9 10 | import sys from PyQt5.QtWidgets import QApplication, QLabel if __name__ == '__main__': app = QApplication(sys.argv) label = QLabel('<font color="red">Hello</font> <h1>World</h1>') #注意这里的font标签不能随意修改 # label.setText('<font color="red">Hello</font> <h1>World</h1>') label.show() sys.exit(app.exec_()) |
有不理解的地方完全没有关系,先记住可以理解的,往下慢慢看,就会越懂越多了。比如可以理解的是QLabel控件的用法,知道这个控件是什么用的,而上方的第4点可以就先记住这么写就好。
1.3 小结¶
-
- QLabel是文本控件,但是也可以用来展示图片(在之后章节讲解);
-
- 可以直接在字符串中添加html代码;
-
- app.exec_()用来执行应用,sys.exit()退出程序(exec就是英文当中的execute【执行】的缩写,这样记就容易了)。
第二章 信号与槽——裁判鸣枪与选手开跑¶
不用多说,信号(signal)与槽(slot)机制很重要。在这里我把信号视作裁判鸣枪,而用于行动的槽函数则视作选手开跑,当裁判鸣枪后(即信号发出),选手就开始往前跑(槽函数启动)。PyQt5中各个对象间或各个对象自身就是通过信号与槽机制来相互通信的,下面来看一个例子。
2.1 通过按钮来改变文本(一个信号连接一个槽)¶
很多程序上是有“开始”按钮的,按下去后按钮上的文本就变成了“停止”。下面就是一个示例(之后的代码都会用类来呈现):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # -*- coding: utf-8 -*- """ Created on Fri May 24 08:47:52 2019 @author: zangz """ import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class Demo(QWidget): # 1 def __init__(self): # 首先找到Demo的父类(比如是类QWidget), #然后把类Demo的对象self转换为类QWigget的对象, #然后“被转换”的类QWidget对象调用自己的__init__函数 super(Demo, self).__init__() # self.button = QPushButton('Start', self) # 2 实例化一个按钮 self.button.clicked.connect(self.change_text) # 3 def change_text(self): print('change text') self.button.setText('Stop') # 4 self.button.clicked.disconnect(self.change_text) # 5 if __name__ == '__main__': app = QApplication(sys.argv) #实例化一个QApplication demo = Demo() #实例化一个demo # 6 demo.show() #显示所有的控件 # 7 sys.exit(app.exec_()) |
-
- 该类继承QWidget,可以将QWidget看作是一种毛坯房,还没有装修,而我们往其中放入QPushButton、QLabel等控件就相当于在装修这间毛坯房。类似的毛坯房还有QMainWindow和QDialog,之后章节再讲述;
-
- 实例化一个QPushButton,因为继承于QWidget,所以self不能忘了(相当于告诉程序这个QPushButton是放在QWidget这个房子中的);
-
- 连接信号与槽函数。self.button就是一个控件,clicked(按钮被点击)是该控件的一个信号,connect()即连接,self.change_text即下方定义的函数(我们称之为槽函数)。所以通用的公式可以是:widget.signal.connect(slot); (控件.信号(什么信号).连接(函数))
-
- 将按钮文本从‘Start’改成‘Stop’;
-
- 信号和槽解绑,解绑后再按按钮你会发现控制台不会再输出‘change text’,如果把这行解绑的代码注释掉,你会发现每按一次按钮,控制台都会输出一次‘change text’;
-
- 实例化Demo类;
-
- 使demo可见,其中的控件自然都可见(除非某控件刚开始设定隐藏)
现在用鸣枪和开跑来分析下上面这个例子:按钮控件是裁判,他鸣枪发出信号(clicked),change_text()槽函数运行就是选手开跑。
运行以上代码,窗口显示如下:
点击之后文本则改变:
2.2 多个信号连接同一个槽¶
2.1 这个示例是用一个信号连接一个槽,现在来看下多个信号连接同一个槽。
QPushButton还有两个信号是pressed和released,这两个信号解释如下:
-
pressed: 当鼠标在button上并点击左键的时候,触发信号 。
-
released: 当鼠标左键被释放的时候触发信号。
所以其实pressed和released两个连起来就是一个完整的clicked
下面用这两个信号来解释如何将多个信号连接到同一个槽:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.button = QPushButton('Start', self) self.button.pressed.connect(self.change_text) # 1 self.button.released.connect(self.change_text) # 2 def change_text(self): if self.button.text() == 'Start': # 3 self.button.setText('Stop') else: self.button.setText('Start') if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
1-2. 将pressed和released信号连接搭配change_text()槽函数上;
-
- 若当前按钮文本为‘Start’,则将文本改为‘Stop’;若为‘Stop’,则改为‘Start’。
-
所以当鼠标点击按钮不放时,发出pressed信号,调用槽函数,将‘Start’文本改为‘Stop’;当鼠标放开后释放released信号,再次调用槽函数,将文本改回‘Start’。
运行截图如下,点击不放时:
放开后:
2.3 一个信号与另外一个信号连接¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.button = QPushButton('Start', self) self.button.pressed.connect(self.button.released) # 1 self.button.released.connect(self.change_text) # 2 def change_text(self): if self.button.text() == 'Start': self.button.setText('Stop') else: self.button.setText('Start') if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
- 1-2. 将pressed信号和released信号连接起来,而released信号则与槽函数连接。这样当点击不放时,pressed信号发出,released信号也会发出,从而启动槽函数。释放鼠标则发出released信号,再次启动槽函数。所以程序运行效果跟2.2小节其实是一样的。
2. 4 一个信号连接多个槽¶
信号都为clicked,然后再多定义几个槽函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(300, 300) # 1 self.setWindowTitle('demo') # 2 self.button = QPushButton('Start', self) self.button.clicked.connect(self.change_text) self.button.clicked.connect(self.change_window_size) # 3 self.button.clicked.connect(self.change_window_title) # 4 def change_text(self): print('change text') self.button.setText('Stop') self.button.clicked.disconnect(self.change_text) def change_window_size(self): # 5 print('change window size') self.resize(500, 500) self.button.clicked.disconnect(self.change_window_size) def change_window_title(self): # 6 print('change window title') self.setWindowTitle('window title changed') self.button.clicked.disconnect(self.change_window_title) if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- 首先在初始化函数中将窗口大小设置为宽300,长300;
-
- 其次将窗口名称设置为‘demo’;
-
3-4. 信号和槽连接,可以看到信号还是clicked,而槽函数多了两个;
-
- 修改窗口大小的槽函数;
-
- 修改窗口名称的槽函数;
-
现在运行点击按钮后,按钮文本会由‘Start’变为‘Stop’,窗口大小从(300, 300)变为(500, 500),窗口标题由‘demo’变为‘window title changed’
以下是运行截图:
点击后则变成:
2.5 自定义信号¶
注意这里将QPushButton换成了QLabel来讲解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import sys from PyQt5.QtCore import pyqtSignal # 1 from PyQt5.QtWidgets import QApplication, QWidget, QLabel class Demo(QWidget): my_signal = pyqtSignal() # 2 def __init__(self): super(Demo, self).__init__() self.label = QLabel('Hello World', self) self.my_signal.connect(self.change_text) # 3 def change_text(self): if self.label.text() == 'Hello World': self.label.setText('Hello PyQt5') else: self.label.setText('Hello World') def mousePressEvent(self, QMouseEvent): # 4 self.my_signal.emit() if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- 需要先导入pyqtSignal;
-
- 实例化一个自定义的信号;
-
- 将自定义的信号连接到自定义的槽函数上;
-
- mousePressEvent()方法是许多控件自带的,这里来自于QWidget。该方法用来监测鼠标是否有按下。现在鼠标若被按下,则会发出自定义的信号。
运行截图如下:
当在窗体空白处按下鼠标,则文本发生变化:
2.6 小结¶
-
- 可以将信号和槽视作裁片鸣枪与选手开跑,信号发出,则相应连接的槽函数启动;
-
- 单个信号可以连接单个槽;单个信号可以连接多个槽;多个信号可以连接单个槽;信号可以与信号连接;也可以自定义信号;
-
- mousePressEvent()方法是许多控件自带的方法,用来监测鼠标是否被按下。
第三章 布局管理¶
把各个控件摆摆好,让整个界面更加有序好看,这就是布局管理器的作用。
3.1 垂直布局QVBoxLayout¶
该布局方式就是将各个控件按从上到下垂直的方式摆放,下面看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.v_layout = QVBoxLayout() # 1 self.v_layout.addWidget(self.user_label) # 2 self.v_layout.addWidget(self.pwd_label) # 3 self.setLayout(self.v_layout) # 4 if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- 实例化一个垂直布局管理器QVBoxLayout;
-
2-3. 通过调用addWidget()方法来将控件一个个添加到垂直布局中,最先添加的出现在最上方;
-
- 将self.v_layout设为整个窗口的最终布局方式。
运行截图如下,可以看出两个标签文本是垂直对齐排列的:
3.2 水平布局QHBoxLayout¶
将控件从左到右依次水平摆放:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QHBoxLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.user_label = QLabel('Username:', self) self.user_line = QLineEdit(self) # 1 self.h_layout = QHBoxLayout() # 2 self.h_layout.addWidget(self.user_label) # 3 self.h_layout.addWidget(self.user_line) # 4 self.setLayout(self.h_layout) # 5 if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- QLineEdit控件就是一个用来进行单行文本输入的框;
-
- 实例化一个水平布局管理器;
-
3-4. 将QLabel和QLineEdit控件添加到水平布局管理器中,先添加的出现在左边;
-
- 将self.h_layout设为整个窗口的最终布局方式。
运行截图如下:
3.3 混合使用QVBoxLayout和QHBoxLayout¶
我们将实现一个用于输入账号密码,并有登陆和注册按钮的小窗口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, \ QHBoxLayout, QVBoxLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.label_v_layout = QVBoxLayout() # 1 self.line_v_layout = QVBoxLayout() # 2 self.button_h_layout = QHBoxLayout() # 3 self.label_line_h_layout = QHBoxLayout() # 4 self.all_v_layout = QVBoxLayout() # 5 self.label_v_layout.addWidget(self.user_label) # 6 self.label_v_layout.addWidget(self.pwd_label) self.line_v_layout.addWidget(self.user_line) self.line_v_layout.addWidget(self.pwd_line) self.button_h_layout.addWidget(self.login_button) self.button_h_layout.addWidget(self.signin_button) self.label_line_h_layout.addLayout(self.label_v_layout) # 7 self.label_line_h_layout.addLayout(self.line_v_layout) self.all_v_layout.addLayout(self.label_line_h_layout) self.all_v_layout.addLayout(self.button_h_layout) self.setLayout(self.all_v_layout) if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
1-3. 实例化三个布局管理器分别用来管理
QLabel,QLineEdit
和QPushButton
; -
4-5. 这两个布局管理器用来管理1-3中的布局,它们添加的不是
QLabel、QLineEdit
或者QPushButton
控件,而是通过addLayout()
方法添加布局管理器。第4行的水平布局管理器将self.label_v_layout
垂直布局和self.line_vlayout
垂直布局这两个布局管理器从左到右依次水平摆放。第5行的垂直布局管理器将self.label_line_h_layout
和self.button_h_layout
垂直从上到下摆放; -
6-7. 添加控件用
addWidght()
,添加布局用addLayout()
。
运行截图如下:
上面的代码是将两个QLabel
用垂直布局方式摆放,将两个QLineEdit
也用垂直布局方式摆放,最后用一个水平布局管理来摆放着两个垂直布局管理器。那换种思路,可以把QLabel和QLineEdit用水平布局方式摆放:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, \ QHBoxLayout, QVBoxLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.user_h_layout = QHBoxLayout() self.pwd_h_layout = QHBoxLayout() self.button_h_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.user_h_layout.addWidget(self.user_label) self.user_h_layout.addWidget(self.user_line) self.pwd_h_layout.addWidget(self.pwd_label) self.pwd_h_layout.addWidget(self.pwd_line) self.button_h_layout.addWidget(self.login_button) self.button_h_layout.addWidget(self.signin_button) self.all_v_layout.addLayout(self.user_h_layout) self.all_v_layout.addLayout(self.pwd_h_layout) self.all_v_layout.addLayout(self.button_h_layout) self.setLayout(self.all_v_layout) if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
相比而言,这种布局方式更加清晰明了。
3.4 表单布局QFormLayout¶
表单布局可以将控件以两列的形式进行排布,左列控件为文本标签,右列为输入型的控件,如QLineEdit
。用这个布局管理器我们可以更加快速方便地构写有表单的界面。我们用QFormLayout
来改写下上面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, \ QHBoxLayout, QVBoxLayout, QFormLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.f_layout = QFormLayout() # 1 self.button_h_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.f_layout.addRow(self.user_label, self.user_line) # 2 self.f_layout.addRow(self.pwd_label, self.pwd_line) self.button_h_layout.addWidget(self.login_button) self.button_h_layout.addWidget(self.signin_button) self.all_v_layout.addLayout(self.f_layout) # 3 self.all_v_layout.addLayout(self.button_h_layout) self.setLayout(self.all_v_layout) if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- 实例化一个QFormLayout控件;
-
- 调用addRow()方法传入QLabel和QLineEdit控件;
-
- 将表单布局添加到总布局中。
-
可以发现代码比之前的更加简洁了。
3.5 网格布局QGridLayout¶
当使用该布局管理器的时候,你可以把整个窗体想象成带有坐标的,然后只用把各个控件放在相应的坐标就好了,请看示例(还是上方的登录框):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, \ QGridLayout, QVBoxLayout, QHBoxLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.grid_layout = QGridLayout() # 1 self.h_layout = QHBoxLayout() self.v_layout = QVBoxLayout() self.grid_layout.addWidget(self.user_label, 0, 0, 1, 1) # 2 self.grid_layout.addWidget(self.user_line, 0, 1, 1, 1) self.grid_layout.addWidget(self.pwd_label, 1, 0, 1, 1) self.grid_layout.addWidget(self.pwd_line, 1, 1, 1, 1) self.h_layout.addWidget(self.login_button) self.h_layout.addWidget(self.signin_button) self.v_layout.addLayout(self.grid_layout) # 3 self.v_layout.addLayout(self.h_layout) # 4 self.setLayout(self.v_layout) if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
这里混合使用QVBoxLayout、QHBoxLayout
和QGridLayou
t来完成布局。
-
- 实例化一个
QGridLayout
布局管理器
- 实例化一个
-
2.
QGridLayout
的addWidget()
方法遵循如下语法形式: -
addWidget(widget, row, column, rowSpan, columnSpan)
widget
就是要添加的控件;row
为第几行,0
代表第一行;column
为第几列,0
代表第一列;rowSpan
表示要让这个控件去占用几行(默认一行);columnSpan
表示要让这个控件去占用几列(默认一列)。 -
在上方程序中,我们将
self.user_label
放在(0, 0)
这个坐标,也就是第一行第一列,占用一行一列;将self.user_line
放在(0, 1)
,即第一行第二列,也就是self.user_label
的右边,占用一行一列;将self.pwd_label
放在(1, 0)
,即第二行第一列,在self.user_label
的正下方,占用一行一列;最后我们将self.pwd_line
放在(1, 1)
,即第二行第二列,占用一行一列。
因为默认都是一行一列,所以也可以写成:
1 2 3 4 | self.grid_layout.addWidget(self.user_label, 0, 0) self.grid_layout.addWidget(self.user_line, 0, 1) self.grid_layout.addWidget(self.pwd_label, 1, 0) self.grid_layout.addWidget(self.pwd_line, 1, 1) |
-
3-4. 最后,程序用垂直布局管理器将一个网格布局和一个水平布局添加进去。
-
当然你也可以尝试单单用QGridLayout来完成这次界面的布局,但是你会发现效果并不会让人很满意,可以自行去试一下。
-
合理的运用这三种布局方式可以在程序设计上事半功倍。
3.6 小结¶
-
- QLineEdit控件为单行文本输入框;
-
- 了解四种布局方式:垂直布局QVBoxLayout、水平布局QHBoxLayout、表单布局QFormLayout和网格布局QGridLayout;
-
- addWidget()方法用来添加控件,addLayout()方法用来添加布局;
-
- 请记住QGridLayout的addWidget()语法形式:
addWidget(widget, row, column, rowSpan, columnSpan)
第四章 QMessageBox消息框¶
在程序中加入各种消息框来提示可以提高用户体验度。
4.1 各种类型的消息框¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.button = QPushButton('information', self) self.button.clicked.connect(self.show_messagebox) # 1 def show_messagebox(self): QMessageBox.information(self, 'Title', 'Content', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) # 2 if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- 实例化一个QPushButton并将clicked信号与自定义的show_messagebox槽函数连接起来,这样点击按钮后,信号发出,槽函数就会启动;
-
- 在槽函数中我们创建了一个信息框(information),基本用法如下:
-
QMessageBox.information(QWidget, ‘Title’, ’Content‘, buttons)
第一个参数填self,表示该信息框属于我们这里的Demo窗口;第二个参数类型为字符串,填入的是该信息框的标题;第三个参数类型也是字符串,填入的是信息框的提示内容;最后个参数为信息框上要添加的按钮,在示例代码中我们添加了Yes、No和Cancel三个按钮,多个按钮之间用|
来连接,常见的按钮种类有以下几种:
1 2 3 4 5 6 7 8 9 10 11 12 13 | QMessageBox.Ok QMessageBox.Yes QMessageBox.No QMessageBox.Close QMessageBox.Cancel QMessage.Open QMessage.Save |
最后运行截图如下:
点击后显示提示框:
注:我是在Mac电脑上运行的,所以这里信息框的标题并没有显示出来。
如果你没有显示指定信息框的按钮,那信息框会自己默认加上合适的按钮上去:
QMessageBox.information(self, 'Title', 'Content') 运行截图如下:
除了信息框(information),还有以下几种,用法都是类似的(请注意消息框上的图标变化):
QMessageBox.question 问答框
QMessageBox.warning 警告框
QMessageBox.critical 错误框
QMessageBox.about 关于框
4.2 与消息框交互¶
在上面的示例中,不管用户按了哪个按钮,程序都用关闭消息框来作出反应。然而用户会希望点击不同按钮,程序作出的反应不同。下面举一个简单的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.button = QPushButton('Click Me!', self) self.button.clicked.connect(self.show_messagebox) def show_messagebox(self): choice = QMessageBox.question(self, 'Change Text?', 'Would you like to change the button text?', QMessageBox.Yes | QMessageBox.No) # 1 if choice == QMessageBox.Yes: # 2 self.button.setText('Changed!') elif choice == QMessageBox.No: # 4 pass if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
-
- 当点击消息框上的某个按钮之后,会返回这个按钮,而这里将返回的按钮结果保存在choice中;有返回值
-
- 若是按下了Yes,则改变按钮的文字;
-
- 若是按下了No,则什么都不做。
运行截图如下:
点击Yes之后,按钮文本改变:
4.3 小结¶
-
- 消息框的种类有: information 信息框;question 问答框; warning 警告框; critical 错误框; about 关于框( 其实还有一个aboutQt框,是专门用来展示Qt软件信息的,这里不再讲述)。
-
- 语法形式(buttons可以不用指定): QMessageBox.information(QWidget, ‘Title’, ’Content‘, buttons)
-
- 在与消息框交互的时候,可以用一个变量来保存消息框返回的按钮信息,接下来再用判断语句来作出不同的反应。
第五章 完善登录框小程序¶
在这一章中,我们会结合前面所学的知识来完善登录框小程序。
为保证代码敲击畅通,可以先引入该程序用到的模块和控件:
1 2 3 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QLabel, QLineEdit, QPushButton, \ QGridLayout, QVBoxLayout, QHBoxLayout, QMessageBox |
5.1 登录界面布局¶
首先完成登录界面布局,请看下方代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QLabel, QLineEdit, QPushButton, \ QGridLayout, QVBoxLayout, QHBoxLayout, QMessageBox class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(300, 100) #设置窗口的大小为300*100 self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.grid_layout = QGridLayout() self.h_layout = QHBoxLayout() self.v_layout = QVBoxLayout() self.layout_init() def layout_init(self): self.grid_layout.addWidget(self.user_label, 0, 0, 1, 1) self.grid_layout.addWidget(self.user_line, 0, 1, 1, 1) self.grid_layout.addWidget(self.pwd_label, 1, 0, 1, 1) self.grid_layout.addWidget(self.pwd_line, 1, 1, 1, 1) self.h_layout.addWidget(self.login_button) self.h_layout.addWidget(self.signin_button) self.v_layout.addLayout(self.grid_layout) self.v_layout.addLayout(self.h_layout) self.setLayout(self.v_layout) if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
程序首先将窗口的宽设为300,长设为100。接着再实例化了几个控件:两个文本标签、两个单行文本输入框和两个按钮。然后搭配使用网格布局管理器、水平布局管理器和垂直布局管理器来完成整个界面的布局。相信看过前面章节的小伙伴已经可以理解上方代码的意思了,这里就不再详细解释。
这里有一点需要注意一下,我把布局管理专门放在了一个函数中layout_init(),然后只用在初始化函数中加上layout_init()中就行。把对不同控件的操作分开来放在相应的函数中,这样写不仅可以让代码更加清晰明了,也方便日后维护。
此时程序运行截图如下:
5.2 完善单行文本输入框和按钮功能¶
该步骤要完成的结果截图如下:
单行文本输入框中有浅灰色的提示文字,Log in按钮刚开始无法点击,只能等两个输入框中都有文本输入的时候才可以进行点击。
首先对输入框功能进行完善。
1 2 3 4 5 6 | def lineedit_init(self): self.user_line.setPlaceholderText('Please enter your username') self.pwd_line.setPlaceholderText('Please enter your password') self.user_line.textChanged.connect(self.check_input_func) self.pwd_line.textChanged.connect(self.check_input_func) |
在未输入前,我们有会看到输入框上就已经有了一行浅灰色的提示文字,就像下面这种:
但点击的话浅灰色的文字就会不见:
这种功能就是通过setPlaceholderText()
方法来实现的。在这里还进行了信号和槽的连接,将QLineEdit
的textChanged()
信号连接到一个自定义的槽self.check_input_func
上。textChanged()
信号会在输入框中文本发生变化的时候发出, 所以槽函数的任务就是判断两个输入框是否都有文字了:
1 2 3 4 5 | def check_input_func(self): if self.user_line.text() and self.pwd_line.text(): self.login_button.setEnabled(True) else: self.login_button.setEnabled(False) |
如果账号框和密码框都有文本(通过text()方法获取输入框文本),那就使登录按钮可用(setEnabled(True)),否则登录按钮不可用。
接下来对按钮进行完善:
1 2 | def pushbutton_init(self): self.login_button.setEnabled(False) |
使刚开始显示的登录按钮不可用,只有等账号框和密码框都有文本的时候才能用(上面的槽函数)。
当我们点击登录按钮的时候,账号框和密码框都有文本了,那点击后肯定是要验证账号密码是否正确:
1 2 3 | def pushbutton_init(self): self.login_button.setEnabled(False) #按下按钮后,不能再次按下 self.login_button.clicked.connect(self.check_login_func) #同时检查账号密码是否正确 |
所以我们将登录按钮的clicked信号和一个用于检查账号密码是否正确的自定义槽函数连接起来:
首先我们在程序的最开始处定义一个全局变量USER_PWD
:
1 2 3 4 5 6 7 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QLabel, QLineEdit, QPushButton, \ QGridLayout, QVBoxLayout, QHBoxLayout, QMessageBox USER_PWD = { 'la_vie': 'password' } #定义账号密码的全局变量 |
该字典的键'la_vie'就是当作账号,值‘password’就当作密码(之后复杂的程序会使用数据库,这里就先简单定义一个全局变量来使用)。
然后定义检查账号密码的槽函数:
1 2 3 4 5 6 7 8 | def check_login_func(self): if USER_PWD.get(self.user_line.text()) == self.pwd_line.text(): QMessageBox.information(self, 'Information', 'Log in Successfully!') else: QMessageBox.critical(self, 'Wrong', 'Wrong Username or Password!') self.user_line.clear() self.pwd_line.clear() |
将账号框的文本当作get()的参数来获取值,然后跟密码框的文本进行比较,若相同则显示信息框提示登录成功,否则显示账号或密码错误。最后无论成功还是失败,都会用clear()方法来清空账号框和密码框。
最后将self.lineedit_init()
和self.pushbutton_init()
放在类的初始化函数__init__()中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(300, 100) self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.grid_layout = QGridLayout() self.h_layout = QHBoxLayout() self.v_layout = QVBoxLayout() self.lineedit_init() # 单行文本输入框 self.pushbutton_init() # 按钮,直接将定义的按钮放在类中进行初始化 self.layout_init() |
此时程序运行截图如下:
输入la_vie和password,点击Log in按钮,则显示信息框提示登录成功。
输入一个错误的账号或错误的密码,就会显示错误框提示账号或密码错误:
点击OK后,账号框和密码框文本都被清空,恢复原状:
5.3 完善注册界面布局及功能¶
接下来就是要完善Sign in按钮这个功能,这里我们想的是点击这个按钮后,会出现一个新的界面用于注册,即下方截图所示:
可以看出该界面一共是由三个QLabel、三个QLineEdit和一个QPushButton组成的,那首先就来完成界面布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class SigninPage(QDialog): def __init__(self): super(SigninPage, self).__init__() self.signin_user_label = QLabel('Username:', self) self.signin_pwd_label = QLabel('Password:', self) self.signin_pwd2_label = QLabel('Password:', self) self.signin_user_line = QLineEdit(self) self.signin_pwd_line = QLineEdit(self) self.signin_pwd2_line = QLineEdit(self) self.signin_button = QPushButton('Sign in', self) self.user_h_layout = QHBoxLayout() self.pwd_h_layout = QHBoxLayout() self.pwd2_h_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.layout_init() def layout_init(self): self.user_h_layout.addWidget(self.signin_user_label) self.user_h_layout.addWidget(self.signin_user_line) self.pwd_h_layout.addWidget(self.signin_pwd_label) self.pwd_h_layout.addWidget(self.signin_pwd_line) self.pwd2_h_layout.addWidget(self.signin_pwd2_label) self.pwd2_h_layout.addWidget(self.signin_pwd2_line) self.all_v_layout.addLayout(self.user_h_layout) self.all_v_layout.addLayout(self.pwd_h_layout) self.all_v_layout.addLayout(self.pwd2_h_layout) self.all_v_layout.addWidget(self.signin_button) self.setLayout(self.all_v_layout) |
这里我们新写一个类,并继承于QDialog(另一个毛坯房)。QDialog就是指对话框,我们平时在软件中点击“打开”或“另存为”而出现的文件对话框就是其中一种。当然你这里也可以同样继承于QWidget,但是QDialog中有一个方法是QWidget所不具有的,稍后来讲述。
上面的代码不再详细解释,相信大家可以看懂了。
首先同样将Sign in
按钮设为不可用(setEnabled(False)
),并且只有当三个输入框中都有文本的时候,才会启用这个按钮。原理跟之前讲的一样:
1 2 3 4 | def lineedit_init(self): self.signin_user_line.textChanged.connect(self.check_input_func) self.signin_pwd_line.textChanged.connect(self.check_input_func) self.signin_pwd2_line.textChanged.connect(self.check_input_func) |
在lineedit_init()中我们将三个输入框的textChanged()信号跟自定义的
check_input_func()
槽函数进行连接;
1 2 3 4 5 | def check_input_func(self): if self.signin_user_line.text() and self.signin_pwd_line.text() and self.signin_pwd2_line.text(): self.signin_button.setEnabled(True) else: self.signin_button.setEnabled(False) |
在槽函数中我们判断三个输入框是否都有文本,有的话则将按钮启用,否则禁用;
1 2 3 | def pushbutton_init(self): #初始化 self.signin_button.setEnabled(False) self.signin_button.clicked.connect(self.check_signin_func) |
然后在pushbutton_init()中先将sign in按钮设为禁用状态,然后将clicked()信号和之后自定义的check_signin_func()槽函数连接起来。因为在点击注册按钮的时候我们要对输入的文本内容进行检查,看符不符合要求;
1 2 3 4 5 6 7 8 9 10 11 12 13 | def check_signin_func(self): if self.signin_pwd_line.text() != self.signin_pwd2_line.text(): QMessageBox.critical(self, 'Wrong', 'Two Passwords Typed Are Not Same!') elif self.signin_user_line.text() not in USER_PWD: USER_PWD[self.signin_user_line.text()] = self.signin_pwd_line.text() QMessageBox.information(self, 'Information', 'Register Successfully') self.close() else: QMessageBox.critical(self, 'Wrong', 'This Username Has Been Registered!') self.signin_user_line.clear() self.signin_pwd_line.clear() self.signin_pwd2_line.clear() |
首先判断两次密码输入框输入的文本是否一致,若不相同的话,则显示错误框提示;当然我们还要对要注册的账号进行判断,如果在字典相对应的键,则说明该账号并没有被注册过,接着将要注册的账号和密码放进字典中,并显示信息框提示注册成功。self.close()是指关闭对话框自身,也就是在信息框按钮被按下去关闭之后,注册界面的对话框也要一起关闭。最后一种情况就是账号已经被注册过了,于是显示错误框来进行提示。
无论哪种情况,最后都会将三个输入框的文本清空掉。
那么最后将self.lineedit_init()和self.pushbutton_init()放在类的初始化函数__init__()中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class SigninPage(QDialog): def __init__(self): super(SigninPage, self).__init__() self.signin_user_label = QLabel('Username:', self) self.signin_pwd_label = QLabel('Password:', self) self.signin_pwd2_label = QLabel('Password:', self) self.signin_user_line = QLineEdit(self) self.signin_pwd_line = QLineEdit(self) self.signin_pwd2_line = QLineEdit(self) self.signin_button = QPushButton('Sign in', self) self.user_h_layout = QHBoxLayout() self.pwd_h_layout = QHBoxLayout() self.pwd2_h_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.lineedit_init() # 单行文本输入框 self.pushbutton_init() # 按钮 self.layout_init() |
5.4 整合登录界面和注册界面¶
但是此时运行程序点击登录界面上的sign in按钮的话,注册界面是显示不出来的,所以我们要将登录界面sign in按钮的clicked()信号和一个自定义的用来显示注册界面的槽函数连接起来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(300, 100) self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.grid_layout = QGridLayout() self.h_layout = QHBoxLayout() self.v_layout = QVBoxLayout() self.lineedit_init() self.pushbutton_init() self.layout_init() self.signin_page = SigninPage() # 实例化SigninPage() |
首先我们在Demo的初始化函数中先实例化了SigninPage;
1 2 3 4 | def pushbutton_init(self): self.login_button.setEnabled(False) self.login_button.clicked.connect(self.check_login_func) self.signin_button.clicked.connect(self.show_signin_page_func) |
接着在处理按钮的函数中将sign in按钮的clicked信号和show_signin_page_func()槽函数进行连接;
1 2 | def show_signin_page_func(self): self.signin_page.exec_() |
在槽函数中,我们用exec_方法来执行注册界面。为什么要使用exec_而不是show()?下面来详细解释下这两个方法的区别:
若使用exec_()的话,那么显示出来的注册界面就是模态的,意思就是当前智能对该注册界面进行操作,只有关闭了该界面才能对其他界面进行操作;若使用show()的话,那注册界面就是非模态的,则在显示了注册界面后,还能同时对登录界面进行操作(QDialog有ecec_()方法,而QWidget没有)。
此时运行截图如下:
点击登录界面的Sign in,显示注册界面,然后输入账号密码,若没有问题,则显示注册成功;
若两次密码输入不一致,则显示错误框提示;
若用户名已经被注册过了,则显示错误框提示。
这里为了方便演示,所以密码框输入的时候都没有用原点替代,我们可以对输入框使用setEchoMode(QLineEdit.Password)方法来完成此功能:
最终完整的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | import sys from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QLabel, QLineEdit, QPushButton, \ QGridLayout, QVBoxLayout, QHBoxLayout, QMessageBox USER_PWD = { 'la_vie': 'password' } class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(300, 100) self.user_label = QLabel('Username:', self) self.pwd_label = QLabel('Password:', self) self.user_line = QLineEdit(self) self.pwd_line = QLineEdit(self) self.login_button = QPushButton('Log in', self) self.signin_button = QPushButton('Sign in', self) self.grid_layout = QGridLayout() self.h_layout = QHBoxLayout() self.v_layout = QVBoxLayout() self.lineedit_init() self.pushbutton_init() self.layout_init() self.signin_page = SigninPage() # 实例化SigninPage() def layout_init(self): self.grid_layout.addWidget(self.user_label, 0, 0, 1, 1) self.grid_layout.addWidget(self.user_line, 0, 1, 1, 1) self.grid_layout.addWidget(self.pwd_label, 1, 0, 1, 1) self.grid_layout.addWidget(self.pwd_line, 1, 1, 1, 1) self.h_layout.addWidget(self.login_button) self.h_layout.addWidget(self.signin_button) self.v_layout.addLayout(self.grid_layout) self.v_layout.addLayout(self.h_layout) self.setLayout(self.v_layout) def lineedit_init(self): self.user_line.setPlaceholderText('Please enter your username') self.pwd_line.setPlaceholderText('Please enter your password') self.pwd_line.setEchoMode(QLineEdit.Password) self.user_line.textChanged.connect(self.check_input_func) self.pwd_line.textChanged.connect(self.check_input_func) def pushbutton_init(self): self.login_button.setEnabled(False) self.login_button.clicked.connect(self.check_login_func) self.signin_button.clicked.connect(self.show_signin_page_func) def check_login_func(self): if USER_PWD.get(self.user_line.text()) == self.pwd_line.text(): QMessageBox.information(self, 'Information', 'Log in Successfully!') else: QMessageBox.critical(self, 'Wrong', 'Wrong Username or Password!') self.user_line.clear() self.pwd_line.clear() def show_signin_page_func(self): self.signin_page.exec_() def check_input_func(self): if self.user_line.text() and self.pwd_line.text(): self.login_button.setEnabled(True) else: self.login_button.setEnabled(False) class SigninPage(QDialog): def __init__(self): super(SigninPage, self).__init__() self.signin_user_label = QLabel('Username:', self) self.signin_pwd_label = QLabel('Password:', self) self.signin_pwd2_label = QLabel('Password:', self) self.signin_user_line = QLineEdit(self) self.signin_pwd_line = QLineEdit(self) self.signin_pwd2_line = QLineEdit(self) self.signin_button = QPushButton('Sign in', self) self.user_h_layout = QHBoxLayout() self.pwd_h_layout = QHBoxLayout() self.pwd2_h_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.lineedit_init() self.pushbutton_init() self.layout_init() def layout_init(self): self.user_h_layout.addWidget(self.signin_user_label) self.user_h_layout.addWidget(self.signin_user_line) self.pwd_h_layout.addWidget(self.signin_pwd_label) self.pwd_h_layout.addWidget(self.signin_pwd_line) self.pwd2_h_layout.addWidget(self.signin_pwd2_label) self.pwd2_h_layout.addWidget(self.signin_pwd2_line) self.all_v_layout.addLayout(self.user_h_layout) self.all_v_layout.addLayout(self.pwd_h_layout) self.all_v_layout.addLayout(self.pwd2_h_layout) self.all_v_layout.addWidget(self.signin_button) self.setLayout(self.all_v_layout) def lineedit_init(self): self.signin_pwd_line.setEchoMode(QLineEdit.Password) self.signin_pwd2_line.setEchoMode(QLineEdit.Password) self.signin_user_line.textChanged.connect(self.check_input_func) self.signin_pwd_line.textChanged.connect(self.check_input_func) self.signin_pwd2_line.textChanged.connect(self.check_input_func) def pushbutton_init(self): self.signin_button.setEnabled(False) self.signin_button.clicked.connect(self.check_signin_func) def check_input_func(self): if self.signin_user_line.text() and self.signin_pwd_line.text() and self.signin_pwd2_line.text(): self.signin_button.setEnabled(True) else: self.signin_button.setEnabled(False) def check_signin_func(self): if self.signin_pwd_line.text() != self.signin_pwd2_line.text(): QMessageBox.critical(self, 'Wrong', 'Two Passwords Typed Are Not Same!') elif self.signin_user_line.text() not in USER_PWD: USER_PWD[self.signin_user_line.text()] = self.signin_pwd_line.text() QMessageBox.information(self, 'Information', 'Register Successfully') self.close() else: QMessageBox.critical(self, 'Wrong', 'This Username Has Been Registered!') self.signin_user_line.clear() self.signin_pwd_line.clear() self.signin_pwd2_line.clear() if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
5.5 小结¶
-
- setPlaceholderText()方法用于在输入框显示浅灰色的提示文本;
-
- ecec_()方法可以让窗口成为模态窗口,而调用show()方法,窗口是非模态的。模态窗口将程序控制权占据,只有对当前窗口关闭后才能操作其他窗口;
-
- QDialog有exec_()方法,而QWidget没有;
-
- 可以用setEchoMode(QLineEdit.Password)将普通输入框中的文字变成原点。
第六章 文本编辑框QTextEdit和文本浏览框QTextBrowser¶
6.1 同步显示文本¶
相信通过这两个控件的中文名大家应该也知道这两个控件的功能是什么了,前者用来编辑文本,后者无法编辑,只用于显示。我们将通过完成以下程序来讲解这两个控件的用法:
左边为QTextEdit控件,右边为QTextBrowser控件。在左边输入文字时,右边会同步显示。请看下方代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | # -*- coding: utf-8 -*- """ Spyder Editor This is a temporary script file. """ import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QTextEdit, QTextBrowser, QHBoxLayout, QVBoxLayout class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.edit_label = QLabel('QTextEdit', self) #实例化一个编辑label self.browser_label = QLabel('QTextBrowser', self) #实例化一个现实label self.text_edit = QTextEdit(self) #实例化一个编辑文本框 self.text_browser = QTextBrowser(self) #实例化一个显示文本框 self.edit_v_layout = QVBoxLayout() self.browser_v_layout = QVBoxLayout() self.all_h_layout = QHBoxLayout() self.layout_init() #布局初始化 self.text_edit_init() def layout_init(self): #布局函数 self.edit_v_layout.addWidget(self.edit_label) self.edit_v_layout.addWidget(self.text_edit) self.browser_v_layout.addWidget(self.browser_label) self.browser_v_layout.addWidget(self.text_browser) self.all_h_layout.addLayout(self.edit_v_layout) self.all_h_layout.addLayout(self.browser_v_layout) self.setLayout(self.all_h_layout) def text_edit_init(self): self.text_edit.textChanged.connect(self.show_text_func) # 1如果text_edit中的文本发生改变的话调用下一个函数 def show_text_func(self): self.text_browser.setText(self.text_edit.toPlainText()) # 2将文本设置为获取到的text_edit中的文本,注意额是使用的toPlainText来获取的文本 if __name__ == '__main__': app = QApplication(sys.argv) demo = Demo() demo.show() sys.exit(app.exec_()) |
程序非常简单。通过实例化两个QLabel、一个QTextEdit以及一个QTextBrowser再通过垂直布局和水平布局就可以完成整个界面。关键点是在信号和槽的连接上。
-
- 将self.text_edit的textChanged信号连接到自定义的槽函数上。也就是说当self.text_edit中的文本发生改变的时候,就会发出textChanged信号,然后调用show_text_func()槽函数。
-
- 在槽函数中我们通过setText()方法将self.text_browser的文本设为self.text_edit的文本,而self.text_edit的文本通过toPlainText()获取,而不是text().
有趣的是,当我们在编辑框中输入Html代码时,右边的浏览框会对其执行:
这里只是简单的介绍这两个控件的使用,不过其实也已经很够用了。等之后用到这两个控件的其他方法时,会再详细介绍。
6.2 小结¶
-
- 顾名思义,
QTextEdit
为用来编辑文本,而QTextBrowser
用来显示文本;
- 顾名思义,
-
setText()
用来设置文本,toPlainText()
用来获取文本,这两个控件都有这些方法;
-
- 浏览框会执行Html代码。